﻿using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Appium.Enums;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading;
using OpenQA.Selenium.Appium.Service;
using SimulationCenter.Servers;

namespace SimulationCenter.Scripts
{
    public abstract class ScriptExecutorBase: IDisposable
    {
        public event EventHandler OnStart;
        public event EventHandler<Exception> OnError;
        public event EventHandler OnComplete;
        public event EventHandler OnStop;
        public event EventHandler OnQuit;
        public event EventHandler OnDispose;

        public ExecutorStatus Status { get; protected set; }

        protected string Serial { get; private set; }
        protected AndroidDriver<AppiumWebElement> Driver { get; private set; }
        protected Size ScreenSize { get; private set; }

        protected bool IsRetry { get; set; } = false;
        protected int RetryCount { get; set; } = 0;
        protected int MaxRetryCount { get; set; } = 3;

        private readonly AppiumOptions _appiumOptions;

        protected AppiumLocalService LocalService { get; private set; }

        protected Uri ServerUri = new Uri(Const.RemoteServer);

        protected ScriptExecutorBase(AppiumLocalService localService, string serial)
        {
            LocalService = localService;
            Serial = serial;

            _appiumOptions = new AppiumOptions();
            InitOptions();

            Status = ExecutorStatus.Ready;
        }

        /// <summary>
        /// 初始化
        /// </summary>
        protected void Initialize()
        {
            Driver = new AndroidDriver<AppiumWebElement>(ServerUri, _appiumOptions, TimeSpan.FromHours(24));
            Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(30);
            ScreenSize = Driver.Manage().Window.Size;

            // PreventSkipFlashPage();
        }

        /// <summary>
        /// 安全启动
        /// </summary>
        public void SafeStart()
        {
            try
            {
                if (Status != ExecutorStatus.Ready && !IsRetry) return;

                OnStart?.Invoke(this, EventArgs.Empty);

                if (IsRetry)
                {
                    Driver?.Quit();
                    Driver?.Dispose();
                }

                Initialize();
                Status = ExecutorStatus.Running;
                Start();
                
                OnComplete?.Invoke(this, EventArgs.Empty);
                Status = ExecutorStatus.Complete;
            }
            catch (Exception e)
            {
                if (RetryCount < MaxRetryCount)
                {
                    IsRetry = true;
                    RetryCount++;
                    SafeStart();
                    return;
                }
                TriggerErrorEvent(e);
            }
            finally
            {
                OnStop?.Invoke(this, EventArgs.Empty);
                Status = ExecutorStatus.Stop;
            }
        }

        /// <summary>
        /// 启动
        /// 启动后处于微信首页
        /// </summary>
        protected abstract void Start();

        /// <summary>
        /// 触发异常事件
        /// </summary>
        /// <param name="e"></param>
        public void TriggerErrorEvent(Exception e)
        {
            OnError?.Invoke(this, e);
            Status = ExecutorStatus.Error;
        }

        /// <summary>
        /// 退出
        /// </summary>
        public void Quit()
        {
            Driver?.Quit();

            OnQuit?.Invoke(this, EventArgs.Empty);

            Status = ExecutorStatus.Quit;
        }

        /// <summary>
        /// 释放
        /// </summary>
        public virtual void Dispose()
        {
            Driver?.Dispose();

            OnDispose?.Invoke(this, EventArgs.Empty);

            Status = ExecutorStatus.Disposed;
        }

        /// <summary>
        /// 切换到联系人 Tab
        /// </summary>
        protected void SwitchToContactsTab()
        {
            try
            {
                var tabs = Driver.FindElements(Const.TabContactsId);

                if (tabs.Any())
                    tabs[1].Click();

                return;
            }
            catch (Exception)
            {
                throw new ScriptExecutorException("切换到联系人Tab", $"未找到对应元素 -> {Const.TabContactsId}");
            }
        }

        /// <summary>
        /// 进入公众号列表
        /// </summary>
        protected void MoveToSubscriptionMenu()
        {
            try
            {
                var menu = Driver.FindElement(Const.SubscriptionMenuId);
                menu.Click();

                Thread.Sleep(TimeSpan.FromSeconds(3));
            }
            catch (Exception)
            {
                throw new ScriptExecutorException("进入公众号列表", $"未找到公众号菜单元素 -> {Const.SubscriptionMenuId}");
            }
        }

        /// <summary>
        /// 确认当前为公众号列表页
        /// </summary>
        protected void EnsureCurrentSubscriptionsPage()
        {
            if (!IsCurrentSubscriptionsPage())
            {
                throw new ScriptExecutorException("进入公众号列表", $"页面定位不准确 -> {Const.SubscriptionsActivity}");
            }
        }

        /// <summary>
        /// 滚动公众号列表
        /// </summary>
        /// <param name="action"></param>
        protected void ScrollSubscriptions(Action action)
        {
            var element = Driver.FindElement(Const.SubscriptionsId);
            var size = element.Size;
            var location = element.Location;
            bool atSubscriptionsBottom;

            do
            {
                action();

                var args = new Dictionary<string, object>
                {
                    ["command"] = "input swipe",
                    ["args"] = $"50 {size.Height - 3} 50 { location.Y + 3} 1000",
                    ["includeStderr"] = true,
                    ["timeout"] = 10000
                };

                Driver.ExecuteScript("mobile:shell", args);

                atSubscriptionsBottom = AtSubscriptionsBottom();

                if (atSubscriptionsBottom)
                    action();
            }
            while (!atSubscriptionsBottom);
        }

        #region privates

        /// <summary>
        /// 初始化
        /// </summary>
        private void InitOptions()
        {
            _appiumOptions.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Android");
            _appiumOptions.AddAdditionalCapability(MobileCapabilityType.DeviceName, Serial);
            _appiumOptions.AddAdditionalCapability(MobileCapabilityType.AutomationName, "UIAutomator2");
            _appiumOptions.AddAdditionalCapability(MobileCapabilityType.NoReset, true);
            _appiumOptions.AddAdditionalCapability(MobileCapabilityType.Udid, Serial);
            _appiumOptions.AddAdditionalCapability(MobileCapabilityType.NewCommandTimeout, 0);
            _appiumOptions.AddAdditionalCapability(AndroidMobileCapabilityType.AppPackage, Const.AppPackage);
            _appiumOptions.AddAdditionalCapability(AndroidMobileCapabilityType.AppActivity, Const.LauncherUi);
            _appiumOptions.AddAdditionalCapability(AndroidMobileCapabilityType.UnicodeKeyboard, true);
            _appiumOptions.AddAdditionalCapability("systemPort", ServerManager.GetFreePort());
            _appiumOptions.AddAdditionalCapability("normalizeTagNames", true);
        }

        /// <summary>
        /// 跳过 Flash 页面
        /// </summary>
        private void PreventSkipFlashPage()
        {
            while (true)
            {
                try
                {
                    Driver.FindElement(Const.FlashPageXpath);

                    Thread.Sleep(TimeSpan.FromSeconds(3));
                }
                catch (Exception)
                {
                    return;
                }
            }
        }

        /// <summary>
        /// 判断当前页是否为公众号列表页
        /// </summary>
        /// <returns></returns>
        protected bool IsCurrentSubscriptionsPage()
        {
            return Const.SubscriptionsActivity.Equals(Driver.CurrentActivity,
                StringComparison.CurrentCultureIgnoreCase);
        }

        /// <summary>
        /// 当前位置是否处于公众号列表的底部
        /// </summary>
        /// <returns></returns>
        private bool AtSubscriptionsBottom()
        {
            try
            {
                Driver.FindElement(Const.SubscriptionsBottomId);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        #endregion
    }
}
